/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file data_manager.c
 * @brief 数据通道回调注册及数据分发管理
 */

#include "nci_module.h"
#include "nci_data.h"
#include "nci_task.h"

volatile static bool s_is_initialized = false;

static void dispatch_data_packet(uint8_t conn_id, uint8_t *data, uint16_t len) {
    tNCI_DATA_CHANNEL *channel = nci_data_find_channel(conn_id);
    if (channel == NULL) {
        NFCLOG_E("channel not avaliable, %d", conn_id);
        return;
    }
    if (osi_mutex_lock(&channel->mutex) != NFC_STATUS_OK) {
        NFCLOG_E("osi_mutex_lock failed");
        return;
    }
    tNCI_DATA_CBACK cback = channel->cback;
    osi_mutex_unlock(&channel->mutex);

    if (!cback) {
        NFCLOG_E("channel(%d) not registered cback", conn_id);
        return;
    }
    cback(conn_id, (const uint8_t*)data, len);
}

static bool handle_data_task(uint32_t what, uint32_t arg1, uint64_t arg2, void* args) {
    switch (what) {
        case NCI_TASK_EVT_DATA_DISPATCH:
            dispatch_data_packet((uint8_t) arg1, (uint8_t *) args, (uint16_t) arg2);
            break;
        default:
            NFCLOG_E("unknown task event, %d", what);
            break;
    }
    return true;
}

static void cleanup(uint32_t what, uint32_t arg1, uint64_t arg2, void* args) {
    (void) arg1;
    (void) arg2;
    switch (what) {
        case NCI_TASK_EVT_DATA_DISPATCH:
            if (args) {
                osi_free(args);
            }
            break;
        default:
            NFCLOG_E("unknown cleanup event, %d", what);
            break;
    }
}

static tNCI_STATUS init(const tNCI_MODULE_INIT_CFG *p_cfg) {
    (void) p_cfg;
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    ret = nci_task_register(handle_data_task, cleanup, NCI_TASK_EVT_DATA_GROUP);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("failed to register data dispatch task, ret = %d", ret);
        return ret;
    }
    ret = nci_data_init(&p_cfg->init_resp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("failed to init data module, ret = %d", ret);
        return ret;
    }
    s_is_initialized = true;
    return ret;
}

static tNCI_STATUS deinit(bool is_shutdown) {
    (void) is_shutdown;
    s_is_initialized = false;
    tNCI_STATUS ret = nci_task_unregister(NCI_TASK_EVT_DATA_GROUP);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("failed to unregister data dispatch task, ret = %d", ret);
    }
    ret = nci_data_deinit();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("failed to deinit data module, ret = %d", ret);
    }
    return NCI_STATUS_OK;
}

tNCI_MODULE g_data_manager = {
    .name = "DATA_MANAGER",
    .state = NCI_MODULE_STATE_DISABLED,
    .init = init,
    .deinit = deinit,
};


/**
 * @brief 向指定通道注册DATA回调函数
 *
 * @note 不允许重复注册
 *
 * @param conn_id 通道ID
 * @param cback 回调函数
 *
 * @return 注册成功返回NCI_STATUS_OK，失败返回错误码
 */
tNCI_STATUS data_register_cback(uint8_t conn_id, tNCI_DATA_CBACK cback) {
    if (!s_is_initialized) {
        return NCI_STATUS_EXT_NOT_INITIALIZED;
    }
    if (!cback) {
        return NCI_STATUS_EXT_INVALID_PARAM;
    }
    tNCI_DATA_CHANNEL *channel = nci_data_find_channel(conn_id);
    if (channel == NULL) {
        NFCLOG_E("channel not avaliable, %d", conn_id);
        return NCI_STATUS_EXT_CHANNEL_NOT_AVAILABLE;
    }
    if (osi_mutex_lock(&channel->mutex) != NFC_STATUS_OK) {
        return NCI_STATUS_EXT_GET_LOCK_FAILED;
    }
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    if (channel->cback) {
        NFCLOG_E("channel(%d) already registered cback:%p", conn_id, channel->cback);
        ret = NCI_STATUS_EXT_REJECTED;
        goto unlock;
    }
    channel->cback = cback;
    ret = NCI_STATUS_OK;
unlock:
    osi_mutex_unlock(&channel->mutex);
    return ret;
}

/**
 * @brief 取消指定通道的DATA回调函数
 *
 * @param conn_id 通道ID
 * @param cback 需要取消的回调函数
 *
 * @return 取消成功返回NCI_STATUS_OK，失败返回错误码
 */
tNCI_STATUS data_unregister_cback(uint8_t conn_id, tNCI_DATA_CBACK cback) {
    if (!s_is_initialized) {
        return NCI_STATUS_EXT_NOT_INITIALIZED;
    }
    if (!cback) {
        return NCI_STATUS_EXT_INVALID_PARAM;
    }
    tNCI_DATA_CHANNEL *channel = nci_data_find_channel(conn_id);
    if (channel == NULL) {
        NFCLOG_E("channel not avaliable, %d", conn_id);
        return NCI_STATUS_EXT_CHANNEL_NOT_AVAILABLE;
    }
    if (osi_mutex_lock(&channel->mutex) != NFC_STATUS_OK) {
        return NCI_STATUS_EXT_GET_LOCK_FAILED;
    }
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    if (!channel->cback) {
        NFCLOG_E("channel(%d) not registered cback", conn_id);
        ret = NCI_STATUS_EXT_REJECTED;
        goto unlock;
    }
    if (channel->cback != cback) {
        NFCLOG_E("channel(%d) registerd cback:%p != %p", conn_id, channel->cback, cback);
        ret = NCI_STATUS_EXT_REJECTED;
        goto unlock;
    }
    channel->cback = NULL;
    ret = NCI_STATUS_OK;
unlock:
    osi_mutex_unlock(&channel->mutex);
    return ret;
}


